﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FileSync.MDL;
using System.IO;
namespace FileSync.MDL
{
    class SyncFileManager
    {
        public delegate void EventHandleMsg(string msg, int count, int length);
        
        public EventHandleMsg onMsg;
        string temFileExtension = ".syncsfm";
        int streamSize = 1024;
        internal ClassLibrary_Public.NetSocket Socket = new ClassLibrary_Public.NetSocket( System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.IP);
        Queue<DAL.Tables.TSyncProject> ScanProject = new Queue<DAL.Tables.TSyncProject>();
        List<DAL.SyncFile> ListFiles = new List<DAL.SyncFile>();
        Queue<MDL.SyncTask> SyncTask = new Queue<MDL.SyncTask>();
        MDL.SyncTask syncTask = null;
        /// <summary>
        /// 扫描目录文件线程
        /// </summary>
        ClassLibrary_Public.CThread threadScanDir = new ClassLibrary_Public.CThread();
        /// <summary>
        /// 同步线程
        /// </summary>
        ClassLibrary_Public.CThread threadSync = new ClassLibrary_Public.CThread();
        public bool PauseSync = false;
        public SyncFileManager()
        {
            Socket.OnErr = (msg) =>
            {
                MDL.CPublic.log.Write(msg, EAF.Log.MsgType.Error);
            };
            ///服务端处理接收的数据
            Socket.OnServerReceive = (data, _socket) =>
            {
                try
                {
                    var pak = MDL.CPublic.ConvertSyncStreamPackage(data);
                    var project = MDL.CPublic.dc.TSyncProject.ToTable().FirstOrDefault(c => c.IsServer && (c.ReadKey == pak.readerKey || c.WriteKey == pak.writerKey));
                    if (project == null) return;
                    switch (pak.type)
                    {
                        case SyncStreamType.FileList://获取服务器端文件列表
                            pak.files = ListFiles.Where(c => c.ProjectID == project.ProjectID).ToArray();
                            Socket.Send(_socket, pak);
                            break;
                        case SyncStreamType.Stream://向客户发送此文件
                            var filePath = Path.Combine(project.RootPath, pak.filePath);
                            FileInfo fInfo = new FileInfo(filePath);
                            if (fInfo.Exists)
                            {
                                if (onMsg != null)
                                    onMsg(string.Format("正在向客户端发送{0}", filePath),0,0);
                                var fInfoModifyTime = fInfo.LastWriteTime;
                                using (var stream = fInfo.OpenRead())
                                {
                                    stream.Position = pak.fileStreamPostion;
                                    do
                                    {
                                        var length = Math.Min(streamSize, stream.Length - pak.fileStreamPostion);
                                        var buffer = new byte[length];
                                        pak.fileStreamPostion = (int)stream.Position;
                                        stream.Read(buffer, 0, (int)length);
                                        pak.fileStreamData = buffer;
                                        pak.fileStreamEnd = stream.Position >= stream.Length;
                                        pak.fileStreamLength = (int)stream.Length;
                                        if (pak.fileStreamEnd)
                                        {
                                            pak.CreationTime = fInfo.CreationTime;
                                            pak.LastAccessTime = fInfo.LastAccessTime;
                                            pak.LastWriteTime = fInfo.LastWriteTime;
                                        }
                                        Socket.Send(_socket, pak);
                                        if (File.GetLastWriteTime(fInfo.FullName) != fInfoModifyTime)
                                        {
                                            pak.fileStreamPostion = 0;
                                        }
                                    }
                                    while (!pak.fileStreamEnd);
                                }
                            }
                            else
                            {
                                pak.isDeleted = true;
                                Socket.Send(_socket, pak);
                            }
                            break;
                    }
                }
                catch (Exception ex)
                {
                    Socket.OnErr(ex.Message);
                }
            };
            ///客户端处理接收的数据
            Socket.OnClientReceive = (data, _socket) =>
            {
                try
                {
                    var pak = MDL.CPublic.ConvertSyncStreamPackage(data);
                    var project = MDL.CPublic.dc.TSyncProject.ToTable().FirstOrDefault(c => !c.IsServer && (c.ReadKey == pak.readerKey || c.WriteKey == pak.writerKey));

                    if (project == null) return;
                    switch (pak.type)
                    {
                        case SyncStreamType.FileList:
                            #region 处理文件列表
                            {
                                if (onMsg != null)
                                    onMsg(string.Format("远程目录{0}获取完成", project.RootPath), 0, 0);
                                var files = ListFiles.Where(c => c.ProjectID == project.ProjectID).ToArray();
                                #region 先删除远程目录中不存在的文件
                                foreach (var file in files)
                                {
                                    if (pak.files.Count(c => c.FilePath == file.FilePath) == 0)
                                        try
                                        {
                                            if (file.isFile)
                                            {
                                                if (File.Exists(Path.Combine(project.RootPath, file.FilePath)))
                                                    File.Delete(Path.Combine(project.RootPath, file.FilePath));
                                            }
                                            else
                                            {
                                                if (Directory.Exists(Path.Combine(project.RootPath, file.FilePath)))
                                                    Directory.Delete(Path.Combine(project.RootPath, file.FilePath), true);
                                            }
                                            ListFiles.Remove(file);
                                        }
                                        catch (Exception ex)
                                        {
                                            Socket.OnErr(ex.Message);
                                        }
                                }
                                #endregion
                                #region 记录要同步的文件
                                if (onMsg != null)
                                    onMsg(string.Format("开始同步远程目录{0}", project.RootPath), 0, 0);
                                foreach (var sFile in pak.files)
                                {
                                    var file = ListFiles.FirstOrDefault(c => c.ProjectID == project.ProjectID && c.FilePath == sFile.FilePath);
                                    if (file == null)
                                        ListFiles.Add(file = new DAL.SyncFile() { ProjectID = project.ProjectID, FilePath = sFile.FilePath, ModifyTime = new DateTime(1900, 1, 1) });
                                    if (file.ModifyTime == sFile.ModifyTime)
                                    {
                                        var filePath = Path.Combine(project.RootPath, file.FilePath, temFileExtension);
                                        if (file.isFile)
                                        {
                                            if (Directory.Exists(filePath))
                                                Directory.Delete(filePath);
                                        }
                                        else
                                        {
                                            if (File.Exists(filePath))
                                                File.Delete(filePath);
                                        }
                                        continue;
                                    }
                                    if (sFile.isFile)
                                    {
                                        if (syncTask == null || syncTask.pak.filePath != sFile.FilePath)
                                        {
                                            if (SyncTask.Count(c => c.socket == _socket && c.pak.filePath == sFile.FilePath) == 0)
                                            {
                                                SyncTask.Enqueue(new MDL.SyncTask() { socket = _socket, pak = new MDL.SyncStreamPackage() { readerKey = project.ReadKey, writerKey = project.WriteKey, filePath = sFile.FilePath, fileStreamPostion = 0, type = SyncStreamType.Stream } });
                                            }
                                        }
                                    }
                                }
                                #endregion
                                if (onMsg != null)
                                    onMsg(string.Format("目录同步完成{0}", project.RootPath), 0, 0);
                            }
                            #endregion
                            break;
                        case SyncStreamType.Stream:
                            #region 处理保存文件
                            {
                                if (syncTask.pak.readerKey == pak.readerKey && syncTask.pak.writerKey == pak.writerKey && syncTask.pak.filePath == pak.filePath )
                                {
                                    var file = ListFiles.FirstOrDefault(c => c.ProjectID == project.ProjectID && c.FilePath == pak.filePath);
                                    if (file == null)
                                    {
                                        ListFiles.Add(file = new DAL.SyncFile() { ProjectID = project.ProjectID, FilePath = pak.filePath, ModifyTime = new DateTime(1900, 1, 1), isFile = true });
                                        project.CountFile++;
                                    }
                                    var filePath = Path.Combine(project.RootPath, pak.filePath);
                                    if (onMsg != null)
                                        onMsg(string.Format("正在同步文件：{0}", pak.filePath), pak.fileStreamPostion, pak.fileStreamLength);
                                    new FileInfo(filePath).Directory.Create();
                                    var filePath_Tem = filePath + temFileExtension;
                                    using (var stream = File.OpenWrite(filePath_Tem))
                                    {
                                        try
                                        {
                                            stream.Position = pak.fileStreamPostion;
                                            if (stream.Position == 0)
                                                stream.SetLength(0);
                                            stream.Write(pak.fileStreamData, 0, pak.fileStreamData.Length);
                                            stream.Close();
                                        }
                                        catch (Exception ex)
                                        {
                                            Socket.OnErr(ex.Message);
                                        }

                                        if (pak.fileStreamEnd)
                                        {
                                            if (File.Exists(filePath))
                                                File.Delete(filePath);
                                            File.Move(filePath_Tem, filePath);
                                            FileInfo fInfo = new FileInfo(filePath);
                                            fInfo.CreationTime = pak.CreationTime.GetValueOrDefault(DateTime.Now);
                                            fInfo.LastAccessTime = pak.LastAccessTime.GetValueOrDefault(DateTime.Now);
                                            fInfo.LastWriteTime = pak.LastWriteTime.GetValueOrDefault(DateTime.Now);
                                            file.ModifyTime = pak.LastWriteTime.GetValueOrDefault(DateTime.Now);
                                            syncTask.statu = MDL.SyncTask.Statu.downLoaded;
                                            if (onMsg != null)
                                                onMsg(string.Format("同步文件：{0}完成", pak.filePath), 0, 0);
                                            break;
                                        }
                                    }
                                }
                                #endregion
                                break;
                            }
                    }
                }
                catch (Exception ex)
                {
                    Socket.OnErr(ex.Message);
                }
            };
            Start();
        }
        public void Start()
        {
            var c1 = MDL.CPublic.dc.TClient.ToTable().FirstOrDefault();
            Socket.StartServer(c1.ClientPort);
            threadSync.start(SyncFile);

        }
        public void Stop()
        {
            threadScanDir.close();
            threadSync.close();
        }
        public void UpdateProject(DAL.Tables.TSyncProject project)
        {
            lock (ScanProject)
                ScanProject.Enqueue(project);
        }
        private void ScanFile()
        {
            foreach (var project in MDL.CPublic.dc.TSyncProject.ToTable().ToArray())
            {
                if (onMsg != null)
                    onMsg(string.Format("正在获取目录{0}列表", project.RootPath), 0, 0);
                DirectoryInfo dirInfo = new DirectoryInfo(project.RootPath);
                {
                    List<DAL.SyncFile> remove_tem = new List<DAL.SyncFile>();
                    foreach (var file in ListFiles.Where(c => c.ProjectID == project.ProjectID).ToArray())
                    {
                        var path = Path.Combine(project.RootPath, file.FilePath);
                        if (file.isFile)
                        {
                            if (!File.Exists(path))
                            {
                                remove_tem.Add(file);
                            }
                        }
                        else
                        {
                            if (!Directory.Exists(path))
                            {
                                remove_tem.Add(file);
                            }
                        }
                    }
                    foreach (var tem in remove_tem)
                        ListFiles.Remove(tem);
                    try
                    {
                        project.CountDir = ListFiles.Count(c => c.ProjectID == project.ProjectID && !c.isFile);
                        project.CountFile = ListFiles.Count(c => c.ProjectID == project.ProjectID && c.isFile);
                        ScanFile(project, dirInfo);
                    }
                    catch (Exception ex)
                    { }
                }
                if (!project.IsServer)
                {
                    var server = MDL.CPublic.dc.TClient.ToTable().FirstOrDefault(c => c.ClientID == project.ClientID);
                    if (server == null) return;
                    if (onMsg != null)
                        onMsg(string.Format("开始同步目录{0}",project.RootPath), 0, 0);
                    Socket.Send(server.ClientIP, server.ClientPort, new MDL.SyncStreamPackage() { type = SyncStreamType.FileList, readerKey = project.ReadKey, writerKey = project.WriteKey });
                    System.Threading.Thread.Sleep(1000);
                }
            }
            if (onMsg != null)
                onMsg("所有目录列表获取完成", 0, 0);
        }
        private void ScanFile(DAL.Tables.TSyncProject project, DirectoryInfo dir)
        {
            foreach (var tem in dir.GetFiles())
            {
                if (tem.Extension == temFileExtension)
                {
                    FileInfo fInfo = new FileInfo(tem.FullName.Replace(temFileExtension, ""));
                    if (fInfo.Exists)
                        if (tem.Length == fInfo.Length)
                            tem.Delete();
                    continue;
                }
                switch (tem.Attributes)
                {
                    case FileAttributes.System:
                        continue;
                }
                string fileName = tem.FullName.Substring(project.RootPath.Length+1);
                var f1 =ListFiles.FirstOrDefault(c => c.ProjectID == project.ProjectID && c.FilePath == fileName );
                if (f1 == null)
                {
                    ListFiles.Add(f1 = new DAL.SyncFile() { ProjectID = project.ProjectID, FilePath = fileName });
                    project.CountFile++;
                }
                else if (f1.ModifyTime == tem.LastWriteTime)
                    continue;
                f1.isFile = true;
                f1.ModifyTime = tem.LastWriteTime;
            }
            foreach (var tem in dir.GetDirectories())
            {
                string fileName = tem.FullName.Substring(project.RootPath.Length + 1);
                var f1 = ListFiles.FirstOrDefault(c => c.ProjectID == project.ProjectID && c.FilePath == fileName);
                if (f1 == null)
                {     ListFiles.Add(f1 = new DAL.SyncFile() { ProjectID = project.ProjectID, FilePath = fileName });
                    project.CountDir++;
                }
                f1.ModifyTime = tem.LastWriteTime;
                f1.isFile = false;

                ScanFile(project, tem);
            }
        }
        private void SyncFile()
        {
            while (true)
            {
                if (Socket.SendTime.AddMinutes(1) < DateTime.Now)
                {
                    Socket.SendTime = DateTime.Now;
                    ScanFile();
                }
                #region 扫描列表
                if (syncTask!=null && syncTask.statu == MDL.SyncTask.Statu.downLoaded)
                    syncTask = null;
                if (syncTask == null)
                    lock (SyncTask)
                        if (SyncTask.Count > 0)
                        {
                            syncTask = SyncTask.Dequeue();
                            syncTask.beginDownLoadTime = DateTime.Now;
                        }
                if (syncTask != null && syncTask.statu == MDL.SyncTask.Statu.waiting)
                {
                    var project = MDL.CPublic.dc.TSyncProject.ToTable().FirstOrDefault(c => c.ReadKey == syncTask.pak.readerKey || c.WriteKey == syncTask.pak.writerKey);
                    if (project != null)
                    {

                        var server = MDL.CPublic.dc.TClient.ToTable().FirstOrDefault(c => c.ClientID == project.ClientID);
                        if (server == null) return;
                        if (onMsg != null)
                            onMsg(string.Format("开始同步文件{0}", syncTask.pak.filePath), 0, 0);
                        syncTask.statu = MDL.SyncTask.Statu.downLoad;
                        Socket.Send(syncTask.socket, syncTask.pak);
                    }
                }
                #endregion
                System.Threading.Thread.Sleep(100);
            }
        }
    }
}
